package br.com.pontoclass.google;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.security.PrivateKey;
import java.util.HashSet;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;

import br.com.pontoclass.main.CredentialsEncrypter;

import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeRequestUrl;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest;
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.auth.oauth2.GoogleTokenResponse;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.common.base.Preconditions;
import com.google.gdata.client.authn.oauth.GoogleOAuthHelper;
import com.google.gdata.client.authn.oauth.GoogleOAuthParameters;
import com.google.gdata.client.authn.oauth.OAuthException;
import com.google.gdata.client.authn.oauth.OAuthHmacSha1Signer;
import com.google.gdata.client.authn.oauth.OAuthParameters.OAuthType;
import com.google.gdata.client.docs.DocsService;
import com.google.gdata.client.spreadsheet.SpreadsheetService;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.common.util.Base64;
import com.google.gdata.util.common.util.Base64DecoderException;


public class GoogleAuthorizer {
    private String application;
    private String userName;
    private String password;
    private String appkey;
    
    @Autowired
    private String spreadsheetGoogleDefaultURL;
    
    @Autowired
    private String spreadsheetGoogleSpreadsheetURL;
    
    @Autowired
    private String spreadsheetGoogleURLForCreation;
    
    private String appRedirURI;

    public GoogleAuthorizer(String appId, String user, String pwd, String pvkFileName, String appkey, String appRedirURI) throws InstantiationException {
        ObjectInputStream stream;
        PrivateKey pk;
        try {
            stream = new ObjectInputStream(Thread.currentThread().getContextClassLoader().getResourceAsStream(pvkFileName));
            pk = Preconditions.checkNotNull(PrivateKey.class.cast(stream.readObject()), 
                                            "It was not possible to fetch the private RSA key. (Fermat is probably turning in his grave and Rivest is getting a heart atack!)");
            this.application = CredentialsEncrypter.decrypt(Base64.decode(appId.getBytes()), pk);
            this.userName = CredentialsEncrypter.decrypt(Base64.decode(user.getBytes()), pk);
            this.password = CredentialsEncrypter.decrypt(Base64.decode(pwd.getBytes()), pk);
            this.appkey = CredentialsEncrypter.decrypt(Base64.decode(appkey.getBytes()), pk);
            this.appRedirURI = CredentialsEncrypter.decrypt(Base64.decode(appRedirURI.getBytes()), pk);
        } catch (Base64DecoderException | ClassNotFoundException | IOException e) {
            throw new InstantiationException(String.format("Could not fetch the private RSA key. Due: '%s'", e.getMessage()));
        }
    }
    
    public DocsService doDocsAuth() throws AuthenticationException{
        DocsService service = new DocsService(application);
        service.setUserCredentials(userName, password);
        try {
            service.setOAuth2Credentials(getCredentials(spreadsheetGoogleDefaultURL, spreadsheetGoogleURLForCreation));
        } catch (OAuthException | IOException e) {
            throw new AuthenticationException(e.getMessage());
        }
        service.setProtocolVersion(DocsService.Versions.V3);
        return service;
    }

    private GoogleCredential getCredentials(final String... spreadsheetGoogleURLs) throws OAuthException, IOException {
        HttpTransport transport = new NetHttpTransport();
        JacksonFactory jsonFactory = new JacksonFactory();

        Set<String> set = new HashSet<String>(){
            private static final long serialVersionUID = 4004643353599280779L;
            {
                for(String url: spreadsheetGoogleURLs) {
                    add(url);
                }
            }
        }; 
        // Step 1: Authorize -->
        String authorizationUrl = new GoogleAuthorizationCodeRequestUrl(application, appRedirURI, set).build();

        // Point or redirect your user to the authorizationUrl.
        System.out.println("Go to the following link in your browser:");
        System.out.println(authorizationUrl);

        // Read the authorization code from the standard input stream.
        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
        System.out.println("What is the authorization code?");
        String code = in.readLine();

        // Step 2: Exchange -->
        GoogleTokenResponse response = new GoogleAuthorizationCodeTokenRequest(transport, jsonFactory, application, appkey, code, appRedirURI).execute();

        // Build a new GoogleCredential instance and return it.
        return new GoogleCredential.Builder()
                                   .setClientSecrets(application, appkey)
                                   .setJsonFactory(jsonFactory)
                                   .setTransport(transport)
                                   .build()
                                   .setAccessToken(response.getAccessToken())
                                   .setRefreshToken(response.getRefreshToken());
    }
    
    public SpreadsheetService doSpreadsheetAuth() throws AuthenticationException{
        SpreadsheetService service = new SpreadsheetService(application);
        service.setUserCredentials(userName, password);
        try {
//            service.setOAuth2Credentials(new GoogleCredential().setAccessToken(getAccessToken(spreadsheetGoogleSpreadsheetURL)));
            service.setOAuth2Credentials(getCredentials(spreadsheetGoogleSpreadsheetURL, spreadsheetGoogleURLForCreation));
        } catch (OAuthException | IOException e) {
            throw new AuthenticationException(e.getMessage());
        }
        service.setProtocolVersion(SpreadsheetService.Versions.V3);
        return service;
    }
    
    @SuppressWarnings("unused")
    private String getAccessToken(String spreadsheetGoogleURL) throws OAuthException {
        GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
        oauthParameters.setOAuthConsumerKey(application);
        oauthParameters.setOAuthConsumerSecret(appkey);
        oauthParameters.setOAuthType(OAuthType.TWO_LEGGED_OAUTH);
        oauthParameters.setScope(spreadsheetGoogleURL);
        GoogleOAuthHelper oauthHelper = new GoogleOAuthHelper(new OAuthHmacSha1Signer());
        oauthHelper.getUnauthorizedRequestToken(oauthParameters);
        String oauthURL = oauthHelper.createUserAuthorizationUrl(oauthParameters);
        return oauthHelper.getAccessToken(oauthParameters);
    }
    
    public String getUserName() {
        return userName;
    }
    
    public String getApplicationId() {
        return application;
    }
}